#include "Buffers.h"

#include "Core/GlobalContext.h"

#include "Platform/OpenGL/OpenGLBuffers.h"

namespace SE
{
	BufferElement::BufferElement(
        const ShaderDataType	_type,
        const String&			_name,
        const Boolean			_normalized)
		: type(_type),
		  name(_name),
          size(GetShaderDataTypeSize(_type)),
          offset(0),
          normalized(_normalized)
	{
	}

	U32 BufferElement::getComponentCount() const
	{
		switch (type)
		{
            default: SE_ASSERT(false, "Unknown ShaderDataType!"); return 0;
			case ShaderDataType::Float:   return 1;
			case ShaderDataType::Float2:  return 2;
			case ShaderDataType::Float3:  return 3;
			case ShaderDataType::Float4:  return 4;
			case ShaderDataType::Mat3:    return 3; // 3 * Float3
			case ShaderDataType::Mat4:    return 4; // 4 * Float4
			case ShaderDataType::Int:     return 1;
			case ShaderDataType::Int2:    return 2;
			case ShaderDataType::Int3:    return 3;
			case ShaderDataType::Int4:    return 4;
			case ShaderDataType::Bool:    return 1;
		}
	}

	BufferLayout::BufferLayout(const std::initializer_list<BufferElement>& elements)
		: mElements(elements)
	{
		calculateOffsetsAndStride();
	}

	U32 BufferLayout::getStride() const
	{
		return mStride;
	}

	const Vector<BufferElement>& BufferLayout::getElements() const
	{
		return mElements;
	}

	Vector<BufferElement>::iterator BufferLayout::begin()
	{
		return mElements.begin();
	}

	Vector<BufferElement>::iterator BufferLayout::end()
	{
		return mElements.end();
	}

	Vector<BufferElement>::const_iterator BufferLayout::begin() const
	{
		return mElements.begin();
	}

	Vector<BufferElement>::const_iterator BufferLayout::end() const
	{
		return mElements.end();
	}
	
	void BufferLayout::calculateOffsetsAndStride()
	{
		U32 offset = 0;
		mStride = 0;
		for (auto& element : mElements)
		{
			element.offset = offset;
			offset += element.size;
			mStride += element.size;
		}
	}

	Ref<VertexBuffer> VertexBuffer::Create(const U32 size, void* buffer)
	{
		switch (RenderAPI::GetAPIType())
		{
			default: SE_ASSERT(false, "Unkown render api type!"); return nullptr;
			case RenderAPIType::OpenGL: return CreateRef<OpenGLVertexBuffer>(size, buffer);
		}
	}

	Ref<IndexBuffer> IndexBuffer::Create(const U32 count, void* buffer)
	{
		switch (RenderAPI::GetAPIType())
		{
			default: SE_ASSERT(false, "Unkown render api type!"); return nullptr;
			case RenderAPIType::OpenGL: return CreateRef<OpenGLIndexBuffer>(count, buffer);
		}
	}

	Ref<VertexArray> VertexArray::Create()
	{
		switch (RenderAPI::GetAPIType())
		{
			default: SE_ASSERT(false, "Unkown render api type!"); return nullptr;
			case RenderAPIType::OpenGL: return CreateRef<OpenGLVertexArray>();
		}
	}

	Ref<FrameBuffer> FrameBuffer::Create(const std::initializer_list<AttachmentLayoutElement>& layout)
	{
		switch (RenderAPI::GetAPIType())
		{
			default: SE_ASSERT(false, "Unkown render api type!"); return nullptr;
			case RenderAPIType::OpenGL: return CreateRef<OpenGLFrameBuffer>(layout);
		}
	}
	Ref<TextureBuffer> TextureBuffer::Create(
		const U32					dimension,
		const U32*					size,
		const RenderInternalFormat	format,
		const WrapMode				wrapMode,
		const MinFilterMode			minFilterMode,
		const MagFilterMode			magFilterMode,
		const Boolean				genMipmaps,
		const Boolean				multisample)
	{
		switch (RenderAPI::GetAPIType())
		{
			default: SE_ASSERT(false, "Unkown render api type!"); return nullptr;
			case RenderAPIType::OpenGL: return CreateRef<OpenGLTextureBuffer>(dimension, size, format, wrapMode, minFilterMode, magFilterMode, genMipmaps, multisample);
		}
	}

	Ref<RenderBuffer> RenderBuffer::Create(
		const U32					dimension,
		const U32*					size,
		const RenderInternalFormat	format)
	{
		switch (RenderAPI::GetAPIType())
		{
			default: SE_ASSERT(false, "Unkown render api type!"); return nullptr;
			case RenderAPIType::OpenGL: return CreateRef<OpenGLRenderBuffer>(dimension, size, format);
		}
	}
} // !namespace SE